package sing.rabbit.consumer.usage;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.context.annotation.Profile;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.Arrays;
import java.util.Map;

import static sing.rabbit.common.configurations.SimpleConfiguration.SIMPLE_QUEUE_NAME;

/**
 * 使用 @RabbitListener 示例1 <br>
 * --- <br>
 * 关于 Message 的打印 与 发送的消息类型 与 contentType 的关联 <br>
 * 当 Provider 使用 template.convertAndSend(queueName(), message); <br>
 * 1. message 类型 为 String 时，接收到的 Message 的 contentType 为 text/plain ，打印显示正确<br>
 * 2. message 类型 为 Message 时，接收到的 Message 的 contentType 为 application/octet-stream （假设没有自行设置）,
 * 此时，因 Message 的 toString 对 body 的处理方法中没有对此 contentType 的处理，所以打印 message 其 body 会显示为 [B@xxx <br>
 * --- <br>
 * 注意事项：<br>
 * 其中有些 Handler 被注释只能当 Provider 发送的消息为某种类型时能处理，当仅有该 Handler 且为其他类型时，会一直循环报错 <br>
 * 此时，消息转换就报错，显然触发不了自动确认，所以消息都还在队列中。下次接收的还是之前的消息。
 * 我开始在测试代码时，因为这个引发问题真的头痛。本来没问题的代码，改回来搁那报错，人要疯了，坏掉。在测试时一定要注意，
 * 先用能正常接收的代码，跑一遍确保队列中消息被清空。再去重新发送想要的（类型的）消息，测试修改后的代码。
 *
 *
 * @author xing
 * @date 2022/9/7 18:12
 */
@Profile("sample_1")
@RabbitListener(queues = SIMPLE_QUEUE_NAME)
@Component
public class SimpleQueueListener {

    /**
     * 仅当 Provider 的 template.convertAndSend(queueName, message); zhong message 为 String 时被使用 <br>
     * 如仅有本 Handler ，当 message 为 Message 类型，会报错：No method found for class [B <br>
     *
     * @param str
     */
//    @RabbitHandler
//    public void receiveStr(String str) {
//        System.out.println("get and is str : " + str);
//    }

    /**
     * 如果要用本方法 isDefault = true 不能少，<br>
     * 当仅有本 Handler 且不加 isDefault = true, 只会报错:，No method found for class [B 或 No Method found for java.lang.String <br>
     * 如果有其他 Handler 且不加 isDefault = true，与没有此 Handler 一样效果(结果)， <br>
     * 由于本身未设置 isDefault = true 判断为不能处理 String 和 Message 类型消息（也可以说为所有类型），
     * 所以不会与其他 Handler 冲突，且在其他 Handler 能处理时，不会调用本方法。
     *
     * @param message
     */
//    @RabbitHandler(isDefault = true)
//    public void receiveMessage(Message message) {
//        System.out.println("get message : " + message);
//    }

    /**
     * 实际为 Message 类型，如果使用 Message 作为参数报错，这种方式可以防止报错。<br>
     * 本 Handler 被用来能处理 String 和 Message 类型的 message ，所以会与前面的 String 类型参数 与 后边的 byte[] 类型参数冲突， 如果一起存在，会报错, 如：Ambiguous methods for payload type: class java.lang.String: receiveStr and receiveObject <br>
     *
     * @param in
     */
//    @RabbitHandler
//    public void receiveObject(Object in) {
//        if( in instanceof Message message) {
//            System.out.println("get object but is message: " + message);
//        }
//    }

    /**
     * 仅当 Provider 发送的 message 为 Message 时被使用<br>
     * 参数来源于 Message 的有参构造器，<br>
     * Provider 发送 Message 类消息，传来的 body 为 byte[] 类型 <br>
     * 如果只有 byte[] body 参数，只能获取到消息体。<br>
     * 不能只有 MessageProperties 参数，。<br>
     *
     * @param body 得到消息体
     * @param messageProperties 得到 其他参数
     */
    @RabbitHandler
    public void receiveBytes(byte[] body, MessageProperties messageProperties) {
        System.out.println("get byte[] and to string is : " + new String(body));
    }


    /**
     * 消息体获取与参数类型有关，在这里 @Payload 没感到用处 <br>
     * 当有消息体接收参数时，可以使用 @Header 注解 <br>
     * <strong>注意：</strong>
     * 如果是通过 @Headers 接收到的参数(不使用会报错，无法从 [B 转换到 Map)，不仅是真的 @Headers 内容，还有 MessageProperties messageProperties 内容，
     * 且不区分，假如原来是 messageProperties[headers={token=token}, a = 1]，现在打印是 {amqp_a=1, token=token} <br>
     * --- <br>
     * 所以最好要么就是用 MessageProperties 作为参数，要么就是用 @Header 作为注解。 <br>
     *
     * 示例 1： <br>
     * <pre class="code">
     * &#064;RabbitHandler(isDefault = true)
     * public void receiveBytes(Message message, @Header String token) {}
     * </pre>
     *
     * 示例 2：
     * <pre class="code">
     * &#064;RabbitHandler
     * public void receiveBytes(byte[] body, @Header String token) {}
     * </pre>
     *
     * 示例 3：
     * <pre class="code">
     * &#064;RabbitHandler
     * public void receiveBytes(@Payload byte[] body, @Header String token) {}
     * </pre>
     *
     */
    public void forIdeaView() {
        // 为了让 IDEA 能转换注释形式，没其他用处
    }
}
